package com.feeyo.prophet.plugins;



import com.feeyo.llm.agent.DecentralizedSSEAgent;
import com.feeyo.llm.agent.dialogue.DialogueAgent;
import com.feeyo.llm.agent.dialogue.FunctionCallingDialoguePolicy;
import com.feeyo.llm.agent.dialogue.nlu.HttpIntentProvider;
import com.feeyo.llm.agent.engine.Engine;
import com.feeyo.llm.agent.engine.llmCompiler.LLMCompiler;
import com.feeyo.llm.agent.engine.reAct.functioncalling.FunctionCallingReAct;
import com.feeyo.llm.agent.engine.reAct.functioncalling.OpenaiFunctionCallingReAct;
import com.feeyo.llm.agent.planning.pddl.HttpPlanProvider;
import com.feeyo.llm.agent.planning.pddl.PlanProvider;
import com.feeyo.llm.agent.tools.HolidayTransferTool;
import com.feeyo.llm.agent.trace.LLMTracer;
import com.feeyo.llm.agent.trace.OpenTelemetryTracer;
import com.feeyo.llm.api.chat.Chat;
import com.feeyo.llm.util.DateTimeUtil;
import com.feeyo.prophet.actions.common.ChartOutputAction;
import com.feeyo.prophet.actions.flight.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;


import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

@Component
public class AgentFactory {
    @Value("${feeyo.tracing.baseUrl}")
    private String traceServerEndpoint;

    @Value("${feeyo.tracing.serviceName}")
    private String traceServiceName;

    @Value("${feeyo.prophet.host}")
    private String host;

    @Value("${feeyo.prophet.port}")
    private int port;

    @Value("${feeyo.price.host}")
    private String priceHost;

    @Value("${feeyo.price.port}")
    private int pricePort;

    @Value("${feeyo.current.prophet.host}")
    private String riakLocalHost;

    @Value("${riak.bucket}")
    private String bucket;


    @Value("${feeyo.flightschedule.host}")
    private String scheduleHost;

    @Value("${feeyo.plan.baseUrl}")
    private String planUrl;

    @Value("${feeyo.plan.serviceName}")
    private String planServiceName;

    @Value("${feeyo.intent.baseUrl}")
    private String intentUrl;

    @Value("${feeyo.intent.serviceName}")
    private String intentServiceName;

    private static final Map<String, AgentContext> contexts = new ConcurrentHashMap<>();

    // 获取全量固定内容
    private static volatile ArrayList<String> follows;

    private static final Long TIME_OUT = 60 * 10 * 1000L;

    private AgentFactory() {
    }


    public static ArrayList<String> getFollows() {
        if (follows == null) {
            synchronized (AgentFactory.class) {
                if (follows == null) {
                    follows = new ArrayList<>() {{
                        this.add("当前国家是中国");
                        this.add("不要向用户询问 问题中使用到的IATA中的如国家二字码, 城市三字码, 机场三字码等");
                        this.add("注意有些IATA城市三字码和IATA机场三字码是不同的");
                        this.add("国外代表中国以外的所有多个国家, 国内代表中国大陆, 地区代表中国港澳台");
                        this.add("航班的单位用\"班\", 不要用\"次\"");
                        this.add("询问涉及到的具体的日期范围,如果没有提供,就默认7天");
                        this.add("回答问题必须包含问题中的所有重要条件, 不可缺漏.");
                        this.add("问题中包含变化趋势的内容,需要对日期按照语义维度进行拆分,并且用拆分的日期进行遍历查询");
                        this.add("回答问题如果有条件不支持必须说明");
                        this.add("如果问题没给具体时间，那么默认时间是现在");
                        this.add("航班号不同与飞机编号和航司，注意区分他们");
                        this.add("遇到今天、昨天、去年、今年、上个月等对于时间的相对描述，请结合现在的时间，转换为具体的时间段");
                        this.add("注意：每年的节假日时间是不同的，如果需要处理多个年份的节假日，请分开处理");
                    }};
                }
            }
        }
        return follows;
    }

    public DialogueAgent getDialogueAgent(String chatId, Chat chat, String model, String apiKey, boolean isDebug) {
        AgentContext context = getOrCreateContext(chatId);
        synchronized (context) {
            OpenTelemetryTracer feeyoTracer = new OpenTelemetryTracer(traceServerEndpoint, traceServiceName, "");
            feeyoTracer.setRegisterActionEndpoint(intentUrl+"/action/register");
            if (context.getDialogueAgent() == null) {
                context.setDecentralizedSSEAgent(null);
                context.setDecentralizedSSEAgent(createDecentralizedSSEAgent(chat, model, isDebug, feeyoTracer));
                FunctionCallingReAct reAct = new OpenaiFunctionCallingReAct(chat, model, LLMTracer.NO_OP);


                String importantText = "在使用工具前,你要分析是一个问题还是多个问题。\n" +
                        "现在时间是:" + DateTimeUtil.yyyy_MM_ddHHmmssFormat(DateTimeUtil.currentTimeMillis()) + "\n"
                        + "如果问题中包含今年、昨天、下个月的时间代词，优先将时间翻译成具体的时间段" + "\n" +
                        "您是一个明航领域的专家啊，请结合民航领域的专业知识，思考遇到的问题";
                reAct.addImportantNote(importantText);
                FunctionCallingDialoguePolicy policy = new FunctionCallingDialoguePolicy(reAct, new HttpIntentProvider(intentUrl, intentServiceName, ""));
                DialogueAgent dialogueAgent = new DialogueAgent(policy, context.getDecentralizedSSEAgent());
                dialogueAgent.setTracer(LLMTracer.NO_OP);
                context.setDialogueAgent(dialogueAgent);
//                IntentManager.setProvider(new HttpIntentProvider(intentUrl, intentServiceName, ""));
            }
            return context.getDialogueAgent();
        }
    }

    private DecentralizedSSEAgent createDecentralizedSSEAgent(Chat chat, String model, boolean isDebug, LLMTracer tracer) {

        PlanProvider planProvider = new HttpPlanProvider(planUrl, planServiceName, "");
        Engine engine = new LLMCompiler(chat, model, tracer, planProvider);
        engine.setUseChinesePrompt(true);
        engine.addAction(new FlightStatisticsAction(host, port));
        engine.addAction(new FlightDynamicAction(host, port, scheduleHost, riakLocalHost, bucket));
        engine.addAction(new AirlineRankAction(host, port));
        engine.addAction(new AirportRankAction(host, port));
        engine.addAction(new FleetAttributesAction(host, port));
        engine.addAction(new FinancialReportAction(host, port));
        engine.addAction(new ChartOutputAction(riakLocalHost, bucket));
        engine.addAction(new FlightPriceAction(host, port, priceHost, pricePort));
        engine.addAction(new HolidayTransferTool());
        engine.setDebug(isDebug);
        DecentralizedSSEAgent instance = new DecentralizedSSEAgent(engine);
        return instance;
    }

    public DecentralizedSSEAgent getDecentralizedSSEAgent(String chatId, Chat chat, String model, boolean isDebug) {
        LLMTracer feeyoTracer = new OpenTelemetryTracer(traceServerEndpoint, traceServiceName, "");
        AgentContext context = getOrCreateContext(chatId);
        synchronized (context) {
            if (context.getDecentralizedSSEAgent() != null) {
                return context.getDecentralizedSSEAgent();
            }
            context.setDecentralizedSSEAgent(createDecentralizedSSEAgent(chat, model, isDebug, feeyoTracer));
            return context.getDecentralizedSSEAgent();
        }
    }

    private AgentContext getOrCreateContext(String chartId) {
        AgentContext context = contexts.computeIfAbsent(chartId, AgentContext::new);
        context.setTimestamp(System.currentTimeMillis());
        return context;
    }


    @Data
    @NoArgsConstructor
    static class AgentContext {
        private DialogueAgent DialogueAgent;

        private Long timestamp;

        DecentralizedSSEAgent decentralizedSSEAgent;

        private String chartId;

        private AgentContext(String chartId) {
            this.chartId = chartId;
        }
    }

    private static void cleanAgent() {
        try {
            long now = System.currentTimeMillis();
            Set<String> charts = contexts.keySet();
            List<String> list = new ArrayList<>(charts);
            for (String cid : list) {
                AgentContext agentContext = contexts.get(cid);
                if (agentContext != null) {
                    if (TIME_OUT + agentContext.getTimestamp() < now) {
                        contexts.remove(cid);
                    }
                }
            }
        } catch (Exception e) {

        }

    }

    static {
        Executors.newScheduledThreadPool(1).scheduleAtFixedRate(AgentFactory::cleanAgent, 0, 10, TimeUnit.MINUTES);
    }
}
